#!/usr/bin/env python3

"""
This script extracts GIFs from the various components that make up BLiPS
and puts them into the DocsFiles repo.

It relies on a directory structure like this:

/<your-path>
    /BiblioPixel   <-- current working directory
    /BiblioPixelAnimations
    /DocsFiles

"""

import argparse, copy, math, pathlib, os, subprocess, sys, tempfile, traceback
cdir = pathlib.Path(__file__).parents[2].absolute()
sys.path.insert(0, str(cdir))
print(sys.path)

from unittest import mock
from bibliopixel.util import data_file, log, walk
from bibliopixel.util.image.extract_gif_lines import extract_gif_lines
from bibliopixel.util.threads import worker
from bibliopixel.project import merge, project


SUFFIXES = '.rst', '.py'
RAISE = False
IMAGE_WIDTH = 320
PADDING_RATIO = 1 / 5
ROOT = pathlib.Path(__file__).absolute().parents[3].joinpath(
    'DocsFiles', 'BiblioPixel')
ROOT_FILES = [('.', 'README.rst')]


def extract_one_gif(args, filename, desc):
    shape = desc.get('shape')
    if shape is None:
        layout = desc.get('layout')
        if not layout:
            raise ValueError('Missing `shape` and `layout` sections')
        width = layout.shape[0]
    else:
        try:
            width = shape[0]
        except TypeError:
            width = shape

    pixel_width = math.ceil(IMAGE_WIDTH / width)
    padding = max(1, int(pixel_width * PADDING_RATIO))

    default = {
        'driver': {
            'filename': filename,
            'render': {
                'pixel_width': pixel_width,
                'padding': padding,
            },
            'time': args.time,
            'typename': '.gif_writer',
        },
        'run': {
            'flat_out': True,
            'fps': args.fps,
        },
        'numbers': 'float',
    }

    merged = merge.merge(default, desc)
    saved_merged = copy.deepcopy(merged)

    path = ROOT.joinpath(filename)
    os.makedirs(str(path.parent), exist_ok=True)
    yml_path = str(path) + '.yml'
    if not args.ignore_cache:
        try:
            df = data_file.load(yml_path)
            if df == merged:
                log.debug('Cached', filename)
                return
        except:
            pass

    default['driver']['filename'] = str(path)
    log.info('Extracting %s', path)
    try:
        pr = project.project(default, desc)
        pr.run()
        data_file.dump(saved_merged, yml_path)
    except KeyboardInterrupt:
        raise
    except:
        log.error('Error in project %s', filename)
        try:
            os.remove(str(path))
        except:
            pass

        if args.fail:
            raise
        else:
            traceback.print_exc()


def extract(args):
    ex = extract_single if args.workers <= 1 else extract_parallel
    return ex(args)


def extract_single(args):
    for filename, desc in all_gif_lines(args):
        extract_one_gif(args, filename, desc)


def extract_parallel(args):
    log.printer('Running extract_gifs with', args.workers, 'workers')

    items = []
    files = set()
    dupes = False
    for filename, desc in all_gif_lines(args):
        if filename in files:
            log.error('Duplicate file %s', filename)
            dupes = True
        else:
            items.append((args, filename, desc))
            files.add(filename)
    if dupes:
        raise ValueError('Duplicate files!')

    worker.work_on(extract_one_gif, items, args.workers)


def all_gif_lines(args):
    files = list(walk.walk_suffix(args.roots, SUFFIXES)) + ROOT_FILES
    for root, input_file in files:
        yield from extract_gif_lines(open(input_file))


def args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--time', '-t', default=10, type=float)
    parser.add_argument(
        '--fps', '-f', default=24, type=float,
        help='frames per second for the animation')
    parser.add_argument(
        'roots', nargs='*', default=('bibliopixel', '_doc'),
        help='Directory roots to search from')
    parser.add_argument(
        '--fail', action='store_true',
        help='Continue going after one image fails')
    parser.add_argument(
        '--workers', '-w', default=6, type=int,
        help='Number of workers to use')
    parser.add_argument(
        '--ignore_cache', '-i', action='store_true',
        help='If true, ignored the cached GIF files')
    return parser.parse_args(sys.argv[1:])


if __name__ == '__main__':
    extract(args())
